/*
 * Copyright (c) 2022  by Junzhao
 * THIS FILE IS PART OF JAVA-Data-Structures-and-Algorithms PROJECT
 * All Rights Reserved.
 */

package 综合计算器.简单四则运算;

/**
 * @Description 使用栈实现简单计算器
 * 思路
 * 1. 通过一个 index  值（索引），来遍历我们的表达式
 * 2. 如果我们发现是一个数字, 就直接入数栈
 * 3. 如果发现扫描到是一个符号,  就分如下情况
 * 3.1 如果发现当前的符号栈为 空，就直接入栈
 * 3.2 如果符号栈有操作符，就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符，
 * 就需要从数栈中pop出两个数,在从符号栈中pop出一个符号，进行运算，将得到结果，入数栈，
 * 然后将当前的操作符入符号栈， 如果当前的操作符的优先级大于栈中的操作符， 就直接入符号栈.
 * 4. 当表达式扫描完毕，就顺序的从 数栈和符号栈中pop出相应的数和符号，并运行.
 * 5. 最后在数栈只有一个数字，就是表达式的结果
 * @Author 俊昭
 * @Date 2022/4/29
 */
public class Calculator {
    private static final int[] numStack = new int[10];// 数字栈
    private static final char[] operatorStack = new char[10];// 运算符栈
    private static int numTop = - 1;// 数字栈顶
    private static int operatorTop = - 1;// 运算符栈顶
    private String formula;

    // 接受一个表达式
    public Calculator(String formula) {
        this.formula = formula;
    }

    /**
     * @description 进行计算操作
     * @author 俊昭
     * @date 2022/4/29
     */
    public void operation() {
        int index = 0;//用于扫描的指针
        char c;//用于保存扫描到的字符

        //加入处理多位数逻辑
        StringBuilder sb = new StringBuilder();


        while (index < formula.length()) {
            // 以此得到每一个formula字符
            c = formula.charAt(index);
            //判断c的类型
            if (isOperator(c)) {
                //是符号
                if (operatorTop == - 1) {
                    // 符号栈空直接入栈
                    pushOperator(c);
                } else {
                    // 不空判断优先级
                    if (priority(c) <= priority(operatorStack[operatorTop])) {
                        // 当前运算符优先级小 从符号栈去除符号进行运算得到结果入数栈
                        pushNumber(calc(popOperator()));
                        //
                        pushOperator(c);
                        show();// 查看效果
                    } else {
                        // 否则直接入栈
                        pushOperator(c);
                    }
                }
            } else {
                // 是数字直接入
                pushNumber(c - '0');//这样的目的是将 字符'1' 转为数字 1 显示

            }
            index++;// 扫描下一个字符
        }
        // 当表达式扫描完毕，就顺序的从 数栈和符号栈中pop出相应的数和符号，并运行.
        while (operatorTop != - 1) {
            pushNumber(calc(popOperator()));
            show();
        }
        // 最后剩的一个数就是结果
        System.out.println(numStack[0]);
    }

    /**
     * @description 向控制台输出当前栈
     * @author 俊昭
     * @date 2022/4/29
     */
    private void show() {
        System.out.print("数字栈:");
        for (int i = 0; i <= numTop; i++) {
            System.out.printf("num[%d]\t%d\t", i, numStack[i]);
        }
        System.out.println();
        System.out.print("运算符栈:");
        for (int i = 0; i <= operatorTop; i++) {
            System.out.printf("oper[%d]\t%c\t", i, operatorStack[i]);
        }
        System.out.println();
    }

    /**
     * @param c 运算符
     * @return 运算结果
     * @description 取出最上边两个数字与传入的运算符进行计算
     * @author 俊昭
     * @date 2022/4/29
     */
    private int calc(char c) {
        int n2 = popNumber();
        int n1 = popNumber();
        switch (c) {
            case '+':
                return n1 + n2;
            case '-':
                return n1 - n2;
            case '*':
                return n1 * n2;
            case '/':
                return n1 / n2;
            default:
                return 0;
        }
    }

    /**
     * @description 将数字入栈
     * @author 俊昭
     * @date 2022/4/29
     */
    private void pushNumber(int number) {
        numTop++;
        numStack[numTop] = number;
    }

    /**
     * @description 将数字出栈
     * @author 俊昭
     * @date 2022/4/29
     */
    private int popNumber() {
        int number = numStack[numTop];
        numTop--;
        return number;
    }

    /**
     * @description 将符号入栈
     * @author 俊昭
     * @date 2022/4/29
     */
    private void pushOperator(char operator) {
        operatorTop++;
        operatorStack[operatorTop] = operator;
    }

    /**
     * @description 将符号出栈
     * @author 俊昭
     * @date 2022/4/29
     */
    private char popOperator() {
        char operator = operatorStack[operatorTop];
        operatorTop--;
        return operator;
    }

    /**
     * @param c 输入的符号
     * @return 优先级
     * @description 输入一个符号返回其优先级
     * @author 俊昭
     * @date 2022/5/3
     */
    private int priority(char c) {
        if (c == '*' || c == '/') {
            return 1;
        } else if (c == '+' || c == '-') {
            return 0;
        } else {
            return - 1;//目前不考虑括号
        }
    }

    /**
     * @param c 输入的字符
     * @return true if c is one of “+ —  * /”
     * @description
     * @author 俊昭
     * @date 2022/5/3
     */
    private boolean isOperator(char c) {
        return c == '*' || c == '/' || c == '+' || c == '-';
    }


}
